home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / samba.idb / usr / samba / src / source / smbpasswd.c.z / smbpasswd.c
Encoding:
C/C++ Source or Header  |  1998-10-28  |  19.8 KB  |  740 lines

  1. /*
  2.  * Unix SMB/Netbios implementation. Version 1.9. smbpasswd module. Copyright
  3.  * (C) Jeremy Allison 1995-1998
  4.  * 
  5.  * This program is free software; you can redistribute it and/or modify it under
  6.  * the terms of the GNU General Public License as published by the Free
  7.  * Software Foundation; either version 2 of the License, or (at your option)
  8.  * any later version.
  9.  * 
  10.  * This program is distributed in the hope that it will be useful, but WITHOUT
  11.  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  12.  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  13.  * more details.
  14.  * 
  15.  * You should have received a copy of the GNU General Public License along with
  16.  * this program; if not, write to the Free Software Foundation, Inc., 675
  17.  * Mass Ave, Cambridge, MA 02139, USA.
  18.  */
  19.  
  20. #include "includes.h"
  21.  
  22. /* 
  23.  * Password changing error codes.
  24.  */
  25.  
  26. struct
  27. {
  28.   int err;
  29.   char *message;
  30. } pw_change_errmap[] =
  31. {
  32.   {5,    "User has insufficient privilege" },
  33.   {86,   "The specified password is invalid" },
  34.   {2226, "Operation only permitted on a Primary Domain Controller"  },
  35.   {2242, "The password of this user has expired." },
  36.   {2243, "The password of this user cannot change." },
  37.   {2244, "This password cannot be used now (password history conflict)." },
  38.   {2245, "The password is shorter than required." },
  39.   {2246, "The password of this user is too recent to change."},
  40.   {0, NULL}
  41. };
  42.  
  43. char *get_error_message(struct cli_state *cli)
  44. {
  45.   static fstring error_message;
  46.   int errclass;
  47.   int errnum;
  48.   int i;
  49.  
  50.   /* 
  51.    * Errors are of two kinds - smb errors,
  52.    * dealt with by cli_errstr, and rap
  53.    * errors, whose error code is in cli.error.
  54.    */
  55.  
  56.   cli_error(cli, &errclass, &errnum);
  57.   if(errclass != 0) 
  58.     return cli_errstr(cli);
  59.  
  60.   slprintf(error_message, sizeof(fstring) - 1, "code %d", cli->error);
  61.       
  62.   for(i = 0; pw_change_errmap[i].message != NULL; i++) {
  63.     if (pw_change_errmap[i].err == cli->error) {
  64.       fstrcpy( error_message, pw_change_errmap[i].message);
  65.       break;
  66.     }
  67.   }
  68.  
  69.   return error_message;
  70. }
  71.  
  72. static int gethexpwd(char *p, char *pwd)
  73. {
  74.     int i;
  75.     unsigned char   lonybble, hinybble;
  76.     char           *hexchars = "0123456789ABCDEF";
  77.     char           *p1, *p2;
  78.     for (i = 0; i < 32; i += 2) {
  79.         hinybble = toupper(p[i]);
  80.         lonybble = toupper(p[i + 1]);
  81.  
  82.         p1 = strchr(hexchars, hinybble);
  83.         p2 = strchr(hexchars, lonybble);
  84.         if (!p1 || !p2)
  85.             return (False);
  86.  
  87.         hinybble = PTR_DIFF(p1, hexchars);
  88.         lonybble = PTR_DIFF(p2, hexchars);
  89.  
  90.         pwd[i / 2] = (hinybble << 4) | lonybble;
  91.     }
  92.     return (True);
  93. }
  94.  
  95. static struct smb_passwd *
  96. _my_get_smbpwnam(FILE * fp, char *name, BOOL * valid_old_pwd, 
  97.         BOOL *got_valid_nt_entry, long *pwd_seekpos)
  98. {
  99.     /* Static buffers we will return. */
  100.     static struct smb_passwd pw_buf;
  101.     static pstring  user_name;
  102.     static unsigned char smbpwd[16];
  103.     static unsigned char smbntpwd[16];
  104.  
  105.     char            linebuf[256];
  106.     unsigned char   c;
  107.     unsigned char  *p;
  108.     long            uidval;
  109.     long            linebuf_len;
  110.  
  111.     /*
  112.      * Scan the file, a line at a time and check if the name matches.
  113.      */
  114.     while (!feof(fp)) {
  115.         linebuf[0] = '\0';
  116.         *pwd_seekpos = ftell(fp);
  117.  
  118.         fgets(linebuf, 256, fp);
  119.         if (ferror(fp))
  120.             return NULL;
  121.  
  122.         /*
  123.          * Check if the string is terminated with a newline - if not
  124.          * then we must keep reading and discard until we get one.
  125.          */
  126.         linebuf_len = strlen(linebuf);
  127.         if (linebuf[linebuf_len - 1] != '\n') {
  128.             c = '\0';
  129.             while (!ferror(fp) && !feof(fp)) {
  130.                 c = fgetc(fp);
  131.                 if (c == '\n')
  132.                     break;
  133.             }
  134.         } else
  135.             linebuf[linebuf_len - 1] = '\0';
  136.  
  137.         if ((linebuf[0] == 0) && feof(fp))
  138.             break;
  139.         /*
  140.          * The line we have should be of the form :-
  141.          * 
  142.          * username:uid:[32hex bytes]:....other flags presently
  143.          * ignored....
  144.          * 
  145.          * or,
  146.          * 
  147.          * username:uid:[32hex bytes]:[32hex bytes]:....ignored....
  148.          * 
  149.          * if Windows NT compatible passwords are also present.
  150.          */
  151.  
  152.         if (linebuf[0] == '#' || linebuf[0] == '\0')
  153.             continue;
  154.         p = (unsigned char *) strchr(linebuf, ':');
  155.         if (p == NULL)
  156.             continue;
  157.         /*
  158.          * As 256 is shorter than a pstring we don't need to check
  159.          * length here - if this ever changes....
  160.          */
  161.         strncpy(user_name, linebuf, PTR_DIFF(p, linebuf));
  162.         user_name[PTR_DIFF(p, linebuf)] = '\0';
  163.         if (!strequal(user_name, name))
  164.             continue;
  165.  
  166.         /* User name matches - get uid and password */
  167.         p++;        /* Go past ':' */
  168.         if (!isdigit(*p))
  169.             return (False);
  170.  
  171.         uidval = atoi((char *) p);
  172.         while (*p && isdigit(*p))
  173.             p++;
  174.  
  175.         if (*p != ':')
  176.             return (False);
  177.  
  178.         /*
  179.          * Now get the password value - this should be 32 hex digits
  180.          * which are the ascii representations of a 16 byte string.
  181.          * Get two at a time and put them into the password.
  182.          */
  183.         p++;
  184.         *pwd_seekpos += PTR_DIFF(p, linebuf);    /* Save exact position
  185.                              * of passwd in file -
  186.                              * this is used by
  187.                              * smbpasswd.c */
  188.         if (*p == '*' || *p == 'X') {
  189.             /* Password deliberately invalid - end here. */
  190.             *valid_old_pwd = False;
  191.             *got_valid_nt_entry = False;
  192.             pw_buf.smb_nt_passwd = NULL;    /* No NT password (yet)*/
  193.  
  194.             /* Now check if the NT compatible password is
  195.                available. */
  196.             p += 33; /* Move to the first character of the line after 
  197.                         the lanman password. */
  198.             if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  199.                 /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  200.                 *got_valid_nt_entry = True;
  201.                 if (*p != '*' && *p != 'X') {
  202.                   if (gethexpwd((char *)p,(char *)smbntpwd))
  203.                     pw_buf.smb_nt_passwd = smbntpwd;
  204.                 }
  205.             }
  206.             pw_buf.smb_name = user_name;
  207.             pw_buf.smb_userid = uidval;
  208.             pw_buf.smb_passwd = NULL;    /* No password */
  209.             return (&pw_buf);
  210.         }
  211.         if (linebuf_len < (PTR_DIFF(p, linebuf) + 33))
  212.             return (False);
  213.  
  214.         if (p[32] != ':')
  215.             return (False);
  216.  
  217.         if (!strncasecmp((char *)p, "NO PASSWORD", 11)) {
  218.           pw_buf.smb_passwd = NULL;    /* No password */
  219.         } else {
  220.           if(!gethexpwd((char *)p,(char *)smbpwd))
  221.             return False;
  222.           pw_buf.smb_passwd = smbpwd;
  223.         }
  224.  
  225.         pw_buf.smb_name = user_name;
  226.         pw_buf.smb_userid = uidval;
  227.         pw_buf.smb_nt_passwd = NULL;
  228.         *got_valid_nt_entry = False;
  229.         *valid_old_pwd = True;
  230.  
  231.         /* Now check if the NT compatible password is
  232.            available. */
  233.         p += 33; /* Move to the first character of the line after 
  234.                     the lanman password. */
  235.         if ((linebuf_len >= (PTR_DIFF(p, linebuf) + 33)) && (p[32] == ':')) {
  236.             /* NT Entry was valid - even if 'X' or '*', can be overwritten */
  237.             *got_valid_nt_entry = True;
  238.             if (*p != '*' && *p != 'X') {
  239.               if (gethexpwd((char *)p,(char *)smbntpwd))
  240.                 pw_buf.smb_nt_passwd = smbntpwd;
  241.             }
  242.         }
  243.         return &pw_buf;
  244.     }
  245.     return NULL;
  246. }
  247.  
  248. /*
  249.  * Print command usage on stderr and die.
  250.  */
  251. static void usage(char *name, BOOL is_root)
  252. {
  253.     if(is_root)
  254.         fprintf(stderr, "Usage is : %s [-a] [-D DEBUGLEVEL] [username] [password]\n\
  255. %s: [-r machine] [-U remote username] [-D DEBUGLEVEL] [username] [password]\n%s: [-h]\n", name, name, name);
  256.     else
  257.         fprintf(stderr, "Usage is : %s [-h] [-D DEBUGLEVEL] [-r machine] [-U remote username] [password]\n", name);
  258.     exit(1);
  259. }
  260.  
  261. int main(int argc, char **argv)
  262. {
  263.   extern char *optarg;
  264.   extern int optind;
  265.   extern int DEBUGLEVEL;
  266.   char *prog_name;
  267.   int             real_uid;
  268.   struct passwd  *pwd;
  269.   fstring         old_passwd;
  270.   uchar           old_p16[16];
  271.   uchar           old_nt_p16[16];
  272.   fstring         new_passwd;
  273.   uchar           new_p16[16];
  274.   uchar           new_nt_p16[16];
  275.   char           *p;
  276.   struct smb_passwd *smb_pwent;
  277.   FILE           *fp;
  278.   BOOL            valid_old_pwd = False;
  279.   BOOL         got_valid_nt_entry = False;
  280.   long            seekpos;
  281.   int             pwfd;
  282.   char            ascii_p16[66];
  283.   char            c;
  284.   int             ch;
  285.   int             ret, i, err, writelen;
  286.   int             lockfd = -1;
  287.   char           *pfile = SMB_PASSWD_FILE;
  288.   char            readbuf[16 * 1024];
  289.   BOOL is_root = False;
  290.   pstring  user_name;
  291.   BOOL           remote_user_name = False;
  292.   char *remote_machine = NULL;
  293.   BOOL         add_user = False;
  294.   BOOL         got_new_pass = False;
  295.   pstring servicesf = CONFIGFILE;
  296.  
  297.   new_passwd[0] = '\0';
  298.   user_name[0] = '\0';
  299.  
  300.   memset(old_passwd, '\0', sizeof(old_passwd));
  301.   memset(new_passwd, '\0', sizeof(new_passwd));
  302.  
  303.   prog_name = argv[0];
  304.  
  305.   TimeInit();
  306.  
  307.   setup_logging(prog_name,True);
  308.   
  309.   charset_initialise();
  310.  
  311.   if (!lp_load(servicesf,True)) {
  312.     fprintf(stderr, "%s: Can't load %s - run testparm to debug it\n", prog_name, servicesf);
  313.   }
  314.     
  315.   codepage_initialise(lp_client_code_page());
  316.  
  317.   /* Get the real uid */
  318.   real_uid = getuid();
  319.   
  320.   /* Check the effective uid */
  321.   if ((geteuid() == 0) && (real_uid != 0)) {
  322.     fprintf(stderr, "%s: Must *NOT* be setuid root.\n", prog_name);
  323.     exit(1);
  324.   }
  325.  
  326.   is_root = (real_uid == 0);
  327.  
  328.   while ((ch = getopt(argc, argv, "ahr:D:U:")) != EOF) {
  329.     switch(ch) {
  330.     case 'a':
  331.       add_user = True;
  332.       break;
  333.     case 'r':
  334.       remote_machine = optarg;
  335.       break;
  336.     case 'D':
  337.       DEBUGLEVEL = atoi(optarg);
  338.       break;
  339.     case 'U':
  340.       remote_user_name = True;
  341.       pstrcpy(user_name, optarg);
  342.       break;
  343.     case 'h':
  344.     default:
  345.       usage(prog_name, is_root);
  346.     }
  347.   }
  348.  
  349.   argc -= optind;
  350.   argv += optind;
  351.  
  352.   if (!is_root && remote_user_name && !remote_machine) {
  353.     fprintf(stderr, "%s: You can only use -U with -r.\n", prog_name);
  354.     usage(prog_name, False);
  355.   }
  356.  
  357.   /*
  358.    * Ensure add_user and remote machine are
  359.    * not both set.
  360.    */
  361.   if(add_user && (remote_machine != NULL))
  362.     usage(prog_name, True);
  363.  
  364.   if( is_root ) {
  365.  
  366.     /*
  367.      * Deal with root - can add a user, but only locally.
  368.      */
  369.  
  370.     switch(argc) {
  371.       case 0:
  372.         break;
  373.       case 1:
  374.         /* If we are root we can change another's password. */
  375.         pstrcpy(user_name, argv[0]);
  376.         break;
  377.       case 2:
  378.         pstrcpy(user_name, argv[0]);
  379.         fstrcpy(new_passwd, argv[1]);
  380.         got_new_pass = True;
  381.         break;
  382.       default:
  383.         usage(prog_name, True);
  384.     }
  385.  
  386.     if(*user_name) {
  387.       if(!remote_machine && ((pwd = getpwnam(user_name)) == NULL)) {
  388.         fprintf(stderr, "%s: User \"%s\" was not found in system password file.\n", 
  389.                     prog_name, user_name);
  390.         exit(1);
  391.       }
  392.     } else {
  393.       if((pwd = getpwuid(real_uid)) != NULL)
  394.         pstrcpy( user_name, pwd->pw_name);
  395.     }
  396.  
  397.   } else {
  398.  
  399.     if(add_user) {
  400.       fprintf(stderr, "%s: Only root can set anothers password.\n", prog_name);
  401.       usage(prog_name, False);
  402.     }
  403.  
  404.     if(argc > 1)
  405.       usage(prog_name, False);
  406.  
  407.     if(argc == 1) {
  408.       fstrcpy(new_passwd, argv[0]);
  409.       got_new_pass = True;
  410.     }
  411.  
  412.     if(!remote_user_name && ((pwd = getpwuid(real_uid)) != NULL))
  413.       pstrcpy( user_name, pwd->pw_name);
  414.  
  415.     /*
  416.      * A non-root user is always setting a password
  417.      * via a remote machine (even if that machine is
  418.      * localhost).
  419.      */
  420.  
  421.     if(remote_machine == NULL)
  422.       remote_machine = "127.0.0.1";
  423.   }    
  424.     
  425.   if (*user_name == '\0') {
  426.     fprintf(stderr, "%s: Unable to get a user name for password change.\n", prog_name);
  427.     exit(1);
  428.   }
  429.  
  430.   /* 
  431.    * If we are root we don't ask for the old password (unless it's on a
  432.    * remote machine.
  433.    */
  434.  
  435.   if (remote_machine != NULL) {
  436.     p = getpass("Old SMB password:");
  437.     fstrcpy(old_passwd, p);
  438.   }
  439.  
  440.   if (!got_new_pass) {
  441.     new_passwd[0] = '\0';
  442.  
  443.     p = getpass("New SMB password:");
  444.  
  445.     strncpy(new_passwd, p, sizeof(fstring));
  446.     new_passwd[sizeof(fstring)-1] = '\0';
  447.  
  448.     p = getpass("Retype new SMB password:");
  449.  
  450.     if (strncmp(p, new_passwd, sizeof(fstring)-1))
  451.     {
  452.       fprintf(stderr, "%s: Mismatch - password unchanged.\n", prog_name);
  453.       exit(1);
  454.     }
  455.   }
  456.   
  457.   if (new_passwd[0] == '\0') {
  458.     printf("Password not set\n");
  459.     exit(0);
  460.   }
  461.  
  462.   /* 
  463.    * Now do things differently depending on if we're changing the
  464.    * password on a remote machine. Remember - a normal user is
  465.    * always using this code, looping back to the local smbd.
  466.    */
  467.  
  468.   if(remote_machine != NULL) {
  469.     struct cli_state cli;
  470.     struct in_addr ip;
  471.     fstring myname;
  472.  
  473.     if(get_myname(myname,NULL) == False) {
  474.       fprintf(stderr, "%s: unable to get my hostname.\n", prog_name );
  475.       exit(1);
  476.     }
  477.  
  478.     if(!resolve_name( remote_machine, &ip)) {
  479.       fprintf(stderr, "%s: unable to find an IP address for machine %s.\n",
  480.               prog_name, remote_machine );
  481.       exit(1);
  482.     }
  483.  
  484.     memset(&cli, '\0', sizeof(struct cli_state));
  485.  
  486.     if (!cli_initialise(&cli) || !cli_connect(&cli, remote_machine, &ip)) {
  487.       fprintf(stderr, "%s: unable to connect to SMB server on machine %s. Error was : %s.\n",
  488.               prog_name, remote_machine, get_error_message(&cli) );
  489.       exit(1);
  490.     }
  491.   
  492.     if (!cli_session_request(&cli, remote_machine, 0x20, myname)) {
  493.       fprintf(stderr, "%s: machine %s rejected the session request. Error was : %s.\n",
  494.               prog_name, remote_machine, get_error_message(&cli) );
  495.       cli_shutdown(&cli);
  496.       exit(1);
  497.     }
  498.   
  499.     cli.protocol = PROTOCOL_NT1;
  500.  
  501.     if (!cli_negprot(&cli)) {
  502.       fprintf(stderr, "%s: machine %s rejected the negotiate protocol. Error was : %s.\n",        
  503.               prog_name, remote_machine, get_error_message(&cli) );
  504.       cli_shutdown(&cli);
  505.       exit(1);
  506.     }
  507.   
  508.     if (!cli_session_setup(&cli, user_name, old_passwd, strlen(old_passwd),
  509.                            "", 0, "")) {
  510.       fprintf(stderr, "%s: machine %s rejected the session setup. Error was : %s.\n",        
  511.               prog_name, remote_machine, get_error_message(&cli) );
  512.       cli_shutdown(&cli);
  513.       exit(1);
  514.     }               
  515.  
  516.     if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
  517.       fprintf(stderr, "%s: machine %s rejected the tconX on the IPC$ share. Error was : %s.\n",
  518.               prog_name, remote_machine, get_error_message(&cli) );
  519.       cli_shutdown(&cli);
  520.       exit(1);
  521.     }
  522.  
  523.     if(!cli_oem_change_password(&cli, user_name, new_passwd, old_passwd)) {
  524.       fprintf(stderr, "%s: machine %s rejected the password change: Error was : %s.\n",
  525.               prog_name, remote_machine, get_error_message(&cli) );
  526.       cli_shutdown(&cli);
  527.       exit(1);
  528.     }
  529.  
  530.     cli_shutdown(&cli);
  531.     exit(0);
  532.   }
  533.  
  534.   /* Calculate the MD4 hash (NT compatible) of the old and new passwords */
  535.   memset(old_nt_p16, '\0', 16);
  536.   E_md4hash((uchar *)old_passwd, old_nt_p16);
  537.   
  538.   memset(new_nt_p16, '\0', 16);
  539.   E_md4hash((uchar *) new_passwd, new_nt_p16);
  540.   
  541.   /* Mangle the passwords into Lanman format */
  542.   old_passwd[14] = '\0';
  543.   strupper(old_passwd);
  544.   new_passwd[14] = '\0';
  545.   strupper(new_passwd);
  546.   
  547.   /*
  548.    * Calculate the SMB (lanman) hash functions of both old and new passwords.
  549.    */
  550.   
  551.   memset(old_p16, '\0', 16);
  552.   E_P16((uchar *) old_passwd, old_p16);
  553.   
  554.   memset(new_p16, '\0', 16);
  555.   E_P16((uchar *) new_passwd, new_p16);
  556.   
  557.   /*
  558.    * Open the smbpaswd file XXXX - we need to parse smb.conf to get the
  559.    * filename
  560.    */
  561.   fp = fopen(pfile, "r+");
  562.   if (!fp && errno == ENOENT) {
  563.       fp = fopen(pfile, "w");
  564.       if (fp) {
  565.           fprintf(fp, "# Samba SMB password file\n");
  566.           fclose(fp);
  567.           fp = fopen(pfile, "r+");
  568.       }
  569.   }
  570.   if (!fp) {
  571.       err = errno;
  572.       fprintf(stderr, "%s: Failed to open password file %s.\n",
  573.           prog_name, pfile);
  574.       errno = err;
  575.       perror(prog_name);
  576.       exit(err);
  577.   }
  578.   
  579.   /* Set read buffer to 16k for effiecient reads */
  580.   setvbuf(fp, readbuf, _IOFBF, sizeof(readbuf));
  581.   
  582.   /* make sure it is only rw by the owner */
  583.   chmod(pfile, 0600);
  584.  
  585.   /* Lock the smbpasswd file for write. */
  586.   if ((lockfd = pw_file_lock(pfile, F_WRLCK, 5)) < 0) {
  587.     err = errno;
  588.     fprintf(stderr, "%s: Failed to lock password file %s.\n",
  589.         prog_name, pfile);
  590.     fclose(fp);
  591.     errno = err;
  592.     perror(prog_name);
  593.     exit(err);
  594.   }
  595.   /* Get the smb passwd entry for this user */
  596.   smb_pwent = _my_get_smbpwnam(fp, user_name, &valid_old_pwd, 
  597.                    &got_valid_nt_entry, &seekpos);
  598.   if (smb_pwent == NULL) {
  599.     if(add_user == False) {
  600.       fprintf(stderr, "%s: Failed to find entry for user %s in file %s.\n",
  601.             prog_name, pwd->pw_name, pfile);
  602.       fclose(fp);
  603.       pw_file_unlock(lockfd);
  604.       exit(1);
  605.     }
  606.  
  607.     /* Create a new smb passwd entry and set it to the given password. */
  608.     {
  609.       int fd;
  610.       int new_entry_length;
  611.       char *new_entry;
  612.       long offpos;
  613.  
  614.       /* The add user write needs to be atomic - so get the fd from 
  615.          the fp and do a raw write() call.
  616.        */
  617.       fd = fileno(fp);
  618.  
  619.       if((offpos = lseek(fd, 0, SEEK_END)) == -1) {
  620.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  621. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  622.         fclose(fp);
  623.         pw_file_unlock(lockfd);
  624.         exit(1);
  625.       }
  626.  
  627.       new_entry_length = strlen(pwd->pw_name) + 1 + 15 + 1 + 
  628.                          32 + 1 + 32 + 1 + strlen(pwd->pw_gecos) + 
  629.                          1 + strlen(pwd->pw_dir) + 1 + 
  630.                          strlen(pwd->pw_shell) + 1;
  631.       if((new_entry = (char *)malloc( new_entry_length )) == 0) {
  632.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  633. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  634.         fclose(fp);
  635.         pw_file_unlock(lockfd);
  636.         exit(1);
  637.       }
  638.  
  639.       slprintf(new_entry, new_entry_length - 1, "%s:%u:", pwd->pw_name, (unsigned)pwd->pw_uid);
  640.       p = &new_entry[strlen(new_entry)];
  641.       for( i = 0; i < 16; i++)
  642.         slprintf(&p[i*2], new_entry_length - (p - new_entry) - (i*2) - 1, "%02X", new_p16[i]);
  643.       p += 32;
  644.       *p++ = ':';
  645.       for( i = 0; i < 16; i++)
  646.         slprintf(&p[i*2], new_entry_length - (p - new_entry) - (i*2) - 1,"%02X", new_nt_p16[i]);
  647.       p += 32;
  648.       *p++ = ':';
  649.       slprintf(p, new_entry_length - (p - new_entry) - 1, "%s:%s:%s\n", pwd->pw_gecos, 
  650.               pwd->pw_dir, pwd->pw_shell);
  651.       if(write(fd, new_entry, strlen(new_entry)) != strlen(new_entry)) {
  652.         fprintf(stderr, "%s: Failed to add entry for user %s to file %s. \
  653. Error was %s\n", prog_name, pwd->pw_name, pfile, strerror(errno));
  654.         /* Remove the entry we just wrote. */
  655.         if(ftruncate(fd, offpos) == -1) {
  656.           fprintf(stderr, "%s: ERROR failed to ftruncate file %s. \
  657. Error was %s. Password file may be corrupt ! Please examine by hand !\n", 
  658.                    prog_name, pwd->pw_name, strerror(errno));
  659.         }
  660.         fclose(fp);
  661.         pw_file_unlock(lockfd);
  662.         exit(1);
  663.       }
  664.       
  665.       fclose(fp);  
  666.       pw_file_unlock(lockfd);  
  667.       exit(0);
  668.     }
  669.   } else {
  670.       /* the entry already existed */
  671.       add_user = False;
  672.   }
  673.  
  674.   /*
  675.    * We are root - just write the new password.
  676.    */
  677.  
  678.   /* Create the 32 byte representation of the new p16 */
  679.   for (i = 0; i < 16; i++) {
  680.     slprintf(&ascii_p16[i * 2], sizeof(ascii_p16) - (i*2) - 1, "%02X", (uchar) new_p16[i]);
  681.   }
  682.   if(got_valid_nt_entry) {
  683.     /* Add on the NT md4 hash */
  684.     ascii_p16[32] = ':';
  685.     for (i = 0; i < 16; i++) {
  686.       slprintf(&ascii_p16[(i * 2)+33], sizeof(ascii_p16) - (i*2) - 32, "%02X", (uchar) new_nt_p16[i]);
  687.     }
  688.   }
  689.   /*
  690.    * Do an atomic write into the file at the position defined by
  691.    * seekpos.
  692.    */
  693.   pwfd = fileno(fp);
  694.   ret = lseek(pwfd, seekpos - 1, SEEK_SET);
  695.   if (ret != seekpos - 1) {
  696.     err = errno;
  697.     fprintf(stderr, "%s: seek fail on file %s.\n",
  698.         prog_name, pfile);
  699.     fclose(fp);
  700.     errno = err;
  701.     perror(prog_name);
  702.     pw_file_unlock(lockfd);
  703.     exit(1);
  704.   }
  705.   /* Sanity check - ensure the character is a ':' */
  706.   if (read(pwfd, &c, 1) != 1) {
  707.     err = errno;
  708.     fprintf(stderr, "%s: read fail on file %s.\n",
  709.         prog_name, pfile);
  710.     fclose(fp);
  711.     errno = err;
  712.     perror(prog_name);
  713.     pw_file_unlock(lockfd);
  714.     exit(1);
  715.   }
  716.   if (c != ':') {
  717.     fprintf(stderr, "%s: sanity check on passwd file %s failed.\n",
  718.         prog_name, pfile);
  719.     fclose(fp);
  720.     pw_file_unlock(lockfd);
  721.     exit(1);
  722.   }
  723.   writelen = (got_valid_nt_entry) ? 65 : 32;
  724.   if (write(pwfd, ascii_p16, writelen) != writelen) {
  725.     err = errno;
  726.     fprintf(stderr, "%s: write fail in file %s.\n",
  727.         prog_name, pfile);
  728.     fclose(fp);
  729.     errno = err;
  730.     perror(prog_name);
  731.     pw_file_unlock(lockfd);
  732.     exit(err);
  733.   }
  734.   fclose(fp);
  735.   pw_file_unlock(lockfd);
  736.   printf("Password changed\n");
  737.   return 0;
  738. }
  739.  
  740.